/**
 * @file STL_XML.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2020-09-15
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

/*
 TiXmlBase：整个TinyXML模型的基类。
 TiXmlAttribute：对应于XML中的元素的属性。
 TiXmlNode：对应于DOM结构中的节点。
 TiXmlComment：对应于XML中的注释。
 TiXmlDeclaration：对应于XML中的申明部分,即<？versiong="1.0" ?>。
 TiXmlDocument：对应于XML的整个文档。
 TiXmlElement：对应于XML的元素。
 TiXmlText：对应于XML的文字部分。
 TiXmlUnknown：对应于XML的未知部分。
 TiXmlHandler：定义了针对XML的一些操作。
 */

#include "STL_XML.hpp"
#include <stdio.h>
#include "tinyxml.h"

namespace STL
{
    // ----------------------------------------------------------------------
    // STDOUT dump and indenting utility functions
    // ----------------------------------------------------------------------
    const unsigned int NUM_INDENTS_PER_SPACE = 2;

    const char *getIndent(unsigned int numIndents)
    {
        static const char *pINDENT = "                                      + ";
        static const unsigned int LENGTH = strlen(pINDENT);
        unsigned int n = numIndents * NUM_INDENTS_PER_SPACE;
        if (n > LENGTH)
            n = LENGTH;

        return &pINDENT[LENGTH - n];
    }

    // same as getIndent but no "+" at the end
    const char *getIndentAlt(unsigned int numIndents)
    {
        static const char *pINDENT = "                                        ";
        static const unsigned int LENGTH = strlen(pINDENT);
        unsigned int n = numIndents * NUM_INDENTS_PER_SPACE;
        if (n > LENGTH)
            n = LENGTH;

        return &pINDENT[LENGTH - n];
    }

    int dump_attribs_to_stdout(TiXmlElement *pElement, unsigned int indent)
    {
        if (!pElement)
            return 0;

        TiXmlAttribute *pAttrib = pElement->FirstAttribute();
        int i = 0;
        int ival;
        double dval;
        const char *pIndent = getIndent(indent);
        printf("\n");
        while (pAttrib)
        {
            printf("%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value());

            if (pAttrib->QueryIntValue(&ival) == TIXML_SUCCESS)
                printf(" int=%d", ival);
            if (pAttrib->QueryDoubleValue(&dval) == TIXML_SUCCESS)
                printf(" d=%1.1f", dval);
            printf("\n");
            i++;
            pAttrib = pAttrib->Next();
        }
        return i;
    }

    void dump_to_stdout(TiXmlNode *pParent, unsigned int indent = 0)
    {
        if (!pParent)
            return;

        TiXmlNode *pChild;
        TiXmlText *pText;
        int t = pParent->Type();
        printf("%s", getIndent(indent));
        int num;

        switch (t)
        {
        case TiXmlNode::DOCUMENT:
            printf("Document");
            break;

        case TiXmlNode::ELEMENT:
            printf("Element [%s]", pParent->Value());
            num = dump_attribs_to_stdout(pParent->ToElement(), indent + 1);
            //            switch(num)
            //        {
            //            case 0:  printf( " (No attributes)"); break;
            //            case 1:  printf( "%s1 attribute", getIndentAlt(indent)); break;
            //            default: printf( "%s%d attributes", getIndentAlt(indent), num); break;
            //        }
            break;

        case TiXmlNode::COMMENT:
            printf("Comment: [%s]", pParent->Value());
            break;

        case TiXmlNode::UNKNOWN:
            printf("Unknown");
            break;

        case TiXmlNode::TEXT:
            pText = pParent->ToText();
            printf("Text: [%s]", pText->Value());
            break;

        case TiXmlNode::DECLARATION:
            printf("Declaration");
            break;
        default:
            break;
        }
        printf("\n");
        for (pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling())
        {
            dump_to_stdout(pChild, indent + 1);
        }
    }

    void STL_XML::testXml(std::string xml)
    {
        printf("%s\n", xml.c_str());
        TiXmlDocument *pDoc = new TiXmlDocument();
        pDoc->Parse(xml.c_str());
        dump_to_stdout(pDoc);
    }

    std::string STL_XML::getValueWithoutXmlns(std::string value)
    {
        size_t pos = value.find_last_of(":");
        if (pos == std::string::npos)
        {
            return value;
        }
        else
        {
            return value.substr(pos + 1);
        }
    }

    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0)
    {
        if (parent == NULL)
            return NULL;
        TiXmlElement *ret = NULL;
        for (TiXmlElement *cur = parent->FirstChildElement(); cur != NULL; cur = cur->NextSiblingElement())
        {
            // printf("%s,%s\n",cur->Value(),key0.c_str());
            if (STL_XML::getValueWithoutXmlns(std::string(cur->Value())) == key0)
            {
                ret = cur;
                break;
            }
        }
        return ret;
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0);
        return STL_XML::getChildElement(newParent, key1);
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1, std::string key2)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0, key1);
        return STL_XML::getChildElement(newParent, key2);
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1, std::string key2, std::string key3)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0, key1, key2);
        return STL_XML::getChildElement(newParent, key3);
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1, std::string key2, std::string key3, std::string key4)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0, key1, key2, key3);
        return STL_XML::getChildElement(newParent, key4);
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1, std::string key2, std::string key3, std::string key4, std::string key5)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0, key1, key2, key3, key4);
        return STL_XML::getChildElement(newParent, key5);
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1, std::string key2, std::string key3, std::string key4, std::string key5, std::string key6)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0, key1, key2, key3, key4, key5);
        return STL_XML::getChildElement(newParent, key6);
    }
    TiXmlElement *STL_XML::getChildElement(TiXmlNode *parent, std::string key0, std::string key1, std::string key2, std::string key3, std::string key4, std::string key5, std::string key6, std::string key7)
    {
        TiXmlElement *newParent = STL_XML::getChildElement(parent, key0, key1, key2, key3, key4, key5, key6);
        return STL_XML::getChildElement(newParent, key7);
    }
    std::string STL_XML::getNodeText(TiXmlNode *node)
    {
        std::string ret = "";
        if (node)
        {
            TiXmlNode *subNode = node->FirstChild();
            if (subNode)
            {
                if (subNode->Type() == TiXmlNode::TEXT)
                {
                    ret = subNode->ToText()->Value();
                }
            }
        }
        return ret;
    }
    void STL_XML::setNodeText(TiXmlNode *node, std::string text)
    {
        if (node == NULL)
            return;
        node->Clear();
        TiXmlText *pValue = new TiXmlText(text.c_str());
        node->LinkEndChild(pValue);
    }

    TiXmlElement *STL_XML::addObjectNode(TiXmlNode *target, std::string key)
    {
        if (!target)
        {
            return nullptr;
        }
        TiXmlElement *tempElement = new TiXmlElement(key.c_str());
        target->LinkEndChild(tempElement);
        return tempElement;
    }

    TiXmlElement *STL_XML::addTextNode(TiXmlElement *target, std::string key, std::string value)
    {
        if (!target)
        {
            return nullptr;
        }
        auto tempObject = STL_XML::addObjectNode(target, key);
        STL_XML::setNodeText(tempObject, value.c_str());
        return tempObject;
    }

    void STL_XML::setElementAttribute(TiXmlElement *target, std::string key, std::string value)
    {
        if (!target)
        {
            return;
        }
        target->RemoveAttribute(key.c_str());
        target->SetAttribute(key.c_str(), value.c_str());
    }

    void STL_XML::LinkEndChildWithClone(TiXmlNode *parent, TiXmlNode *child)
    {
        if (parent && child)
        {
            TiXmlNode *newChild = child->Clone();
            parent->LinkEndChild(newChild);
        }
    }

    // TiXmlElement *STL_XML::insert_child(TiXmlElement *root, const char *key, const std::string &value)
    // {
    //     if (root == nullptr || key == nullptr)
    //         return nullptr;

    //     TiXmlElement *child_ele = s_Doc.NewElement(key);
    //     if (!value.empty())
    //     {
    //         child_ele->SetText(value.c_str());
    //     }
    //     root->InsertEndChild(child_ele);

    //     return child_ele;
    // }

    TiXmlDocument *STL_XML::newPointer_ParseXmlString(std::string xml)
    {
        TiXmlDocument *pDoc = new TiXmlDocument();
        pDoc->Parse(xml.c_str());
        if (pDoc->Error())
        {
            delete pDoc;
            return NULL;
        }
        return pDoc;
    }

    TiXmlElement *STL_XML::getRootElementFromDocument(TiXmlDocument *doc)
    {
        TiXmlElement *root = doc->RootElement();
        if (root && root->Type() == TiXmlNode::ELEMENT)
        {
            return root;
        }
        else
        {
            return NULL;
        }
    }

    TiXmlDeclaration *STL_XML::getDeclarationfromDocument(TiXmlDocument *doc)
    {
        TiXmlNode *pChild = nullptr;
        for (pChild = doc->FirstChild(); pChild != 0; pChild = pChild->NextSibling())
        {
            if (pChild->Type() == TiXmlNode::DECLARATION)
            {
                return pChild->ToDeclaration();
            }
        }
        return nullptr;
    }

    // TiXmlElement *STL_XML::newPointer_getRootElementFormXmlString(std::string xml)
    // {
    //     TiXmlDocument doc = TiXmlDocument();
    //     doc.Parse(xml.c_str());
    //     if (doc.Error())
    //     {
    //         return NULL;
    //     }
    //     else
    //     {
    //         TiXmlElement *root = doc.RootElement();
    //         if (root && root->Type() == TiXmlNode::ELEMENT)
    //         {
    //             return root->Clone()->ToElement();
    //         }
    //         else
    //         {
    //             return NULL;
    //         }
    //     }
    // }

    std::string STL_XML::getStreamXmlString(TiXmlDocument *doc)
    {
        std::string ret = "";
        if (doc)
        {
            TiXmlPrinter printer;
            printer.SetStreamPrinting();
            doc->Accept(&printer);
            ret = printer.CStr();
        }
        return ret;
    }

    std::string STL_XML::getFormatXmlString(TiXmlDocument *doc)
    {
        std::string ret = "";
        if (doc)
        {
            TiXmlPrinter printer;
            doc->Accept(&printer);
            ret = printer.CStr();
        }
        return ret;
    }

} // namespace STL
